<!DOCTYPE html>
<html lang="zh-cn">
  <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="author" content="Zhou Wei <zromyk@163.com>">
  <title>协程</title>
  <link rel="shortcut icon" href="/favicon.ico">
  <link rel="stylesheet" href="/style/html/pure.css">
  <link rel="stylesheet" href="/style/html/main.css">
  <link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.css">
  <!-- <link rel="stylesheet" href="https://apps.bdimg.com/libs/highlight.js/9.1.0/styles/default.min.css"> -->
<link rel="stylesheet" href="/style/article/highlight/default.min.css">
<link rel="stylesheet" href="/style/article/pell-1.0.6/dist/pell.css">

</head>
<body>
  <div id="menu-background"></div>
  <div id="menu">
    <div class="pure-menu pure-menu-horizontal">
  <ul class="pure-menu-list block-middle">
    <li class="pure-menu-item">
  <a class="pure-menu-heading" href="/index.html">ZROMYK</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/index.html">主页</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/archive/index.html">归档</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/download/index.html">下载</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/feedback/index.html">反馈</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/about/index.html">关于我</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="https://github.com/zromyk"><i class="fa fa-github" style="font-size:32px"></i></a>
</li>

  </ul>
</div>

  </div>
  <div id="layout">
    <div class="content">
      <div id="nav">
    <div id="navigation" class="navigation">
  <ul class="pure-menu-list">
    <li class="pure-menu-item">
  <a class="pure-menu-link nav1" onclick="animateByNav()" href="#_1">一、协程</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav1" onclick="animateByNav()" href="#ucontext">ucontext</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#ucontext_1">ucontext 的一个简单示例</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#ucontext_2">ucontext 组件内容</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#_2">简单说来</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav1" onclick="animateByNav()" href="#c-protothreads">一个“蝇量级” C 语言协程库：<span style="overflow-x: auto; max-width:100%; display:inline;"><code>Protothreads</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#c-yield">C 语言的 &ldquo;<span style="overflow-x: auto; max-width:100%; display:inline;"><code>yield</code></span> 语义&rdquo;</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#protothreads">Protothreads的原语和组件</a>
</li>

  </ul>
</div>

</div>
<div id="content-articles">
  <h1 id="协程" class="content-subhead">协程</h1>
  <p>
    <span>2021-04-17</span>
    <span><span class="post-category post-category-cpp">C++</span></span>
    <button id="button-markdownEditor" class="pure-button" onclick="markdownEditor()">启用编辑</button>
    <button id="button-save" class="pure-button" onclick="save()">保存</button>
  </p>
  <div id="content-articles-markdownEditor" style="display: none;">
    <h1>编辑 power by pell</h1>
    <div id="editor" class="pell"></div>
    <div style="margin-top:20px;">
        <h3>Text output:</h3>
        <div id="text-output"></div>
    </div>
    <div style="margin-top:20px;">
        <h3>HTML output:</h3>
        <pre id="html-output"></pre>
    </div>
  </div>
  <div id="content-articles-markdown">
    <blockquote class="content-quote">
<p>转载：<a href="https://blog.csdn.net/qq910894904/article/details/41911175">ucontext-人人都可以实现的简单协程库</a></p>
</blockquote>
<h1 id="_1">一、协程</h1>
<p>协程是一种 <strong>用户态</strong> 的轻量级线程。</p>
<p>首先我们可以看看有哪些语言已经具备协程语义：</p>
<ul>
<li>比较重量级的有 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>C#</code></span>、<span style="overflow-x: auto; max-width:100%; display:inline;"><code>erlang</code></span>、<span style="overflow-x: auto; max-width:100%; display:inline;"><code>golang*</code></span></li>
<li>轻量级有 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>python</code></span>、<span style="overflow-x: auto; max-width:100%; display:inline;"><code>lua</code></span>、<span style="overflow-x: auto; max-width:100%; display:inline;"><code>javascript</code></span>、<span style="overflow-x: auto; max-width:100%; display:inline;"><code>ruby</code></span></li>
<li>还有函数式的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>scala</code></span>、<span style="overflow-x: auto; max-width:100%; display:inline;"><code>scheme</code></span>等。</li>
</ul>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>C/C++</code></span> 不直接支持协程语义，但有不少开源的协程库，如：[来自腾讯的开源协程库：<span style="overflow-x: auto; max-width:100%; display:inline;"><code>libco</code></span>]：<a href="https://gitee.com/mirrors/libco?utm_source=alading&amp;utm_campaign=repo">gitee</a>，<a href="https://github.com/canonical/libco">github</a></p>
<p>大概有四种实现协程的方式：</p>
<ol>
<li>利用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>glibc</code></span> 的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>ucontext</code></span> 组件（<a href="https://github.com/cloudwu/coroutine/">云风的一个C语言同步协程库：<span style="overflow-x: auto; max-width:100%; display:inline;"><code>coroutine</code></span></a>）</li>
<li>使用汇编代码来切换上下文</li>
<li>利用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>C</code></span> 语言语法 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>switch-case</code></span> 的奇淫技巧来实现（<a href="http://coolshell.cn/articles/10975.html">一个“蝇量级” C 语言协程库：<span style="overflow-x: auto; max-width:100%; display:inline;"><code>Protothreads</code></span></a>)</li>
<li>利用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>C</code></span> 语言的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>setjmp</code></span> 和 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>longjmp</code></span>（一种协程的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>C/C++</code></span> 实现，要求函数里面使用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>static local</code></span> 的变量来保存协程内部的数据）</li>
</ol>
<p>携程的执行过程类似于：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-python">def A():
    print '1'
    print '2'
    print '3'

def B():
    print 'x'
    print 'y'
    print 'z'
</code></span></code></pre>
<p>假设由协程执行，在执行 A 的过程中，可以随时中断，去执行 B，B 也可能在执行过程中中断再去执行 A，结果 <strong>可能</strong> 是：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code>1
2
x
y
3
z
</code></span></code></pre>
<h1 id="ucontext">ucontext</h1>
<h2 id="ucontext_1">ucontext 的一个简单示例</h2>
<p>利用 ucontext 提供的四个函数，可以在一个进程中实现用户级的线程切换。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">getcontext()
setcontext()
makecontext()
swapcontext()
</code></span></code></pre>
<p>一个简单的例子：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#include &lt;stdio.h&gt;
#include &lt;ucontext.h&gt;
#include &lt;unistd.h&gt;

int main(int argc, const char *argv[])
{
    ucontext_t context;

    getcontext(&amp;context);
    puts(&quot;Hello world&quot;);
    sleep(1);
    setcontext(&amp;context);
    return 0;
}
// gcc example.c -o example
</code></span></code></pre>
<p>编译运行后的结果如下：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">$ ./example
Hello world
Hello world
Hello world
...
^C
$
</code></span></code></pre>
<p>可以看到程序在输出第一个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>"Hello world"</code></span> 后并没有退出程序，而是持续不断的输出 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>"Hello world"</code></span>。</p>
<p>其实是程序通过 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>getcontext()</code></span>先保存了一个上下文，然后输出 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>"Hello world"</code></span>，再通过 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>setcontext()</code></span> 恢复到<span style="overflow-x: auto; max-width:100%; display:inline;"><code>getcontext()</code></span> 的地方，重新执行代码，所以导致程序不断的输出 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>"Hello world"</code></span>.</p>
<h2 id="ucontext_2">ucontext 组件内容</h2>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// &lt;ucontext.h&gt;

/*
mcontext_t 类型与机器相关，并且不透明
ucontext_t 结构体则至少拥有以下几个域:
*/

typedef struct ucontext {
    struct ucontext *uc_link;       // 当前上下文运行终止时系统会恢复 uc_link 指向的上下文.
    sigset_t         uc_sigmask;    // uc_sigmask 为该上下文中的阻塞信号集合.
    stack_t          uc_stack;      // uc_stack 为该上下文中使用的栈.
    mcontext_t       uc_mcontext;   // uc_mcontext 保存的上下文的特定机器表示,
    ...                             //              包括调用线程的特定寄存器等.
} ucontext_t;
</code></span></code></pre>
<p>下面详细介绍四个函数：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int getcontext(ucontext_t *ucp);        // 初始化 ucp 结构体，将当前的上下文保存到 ucp 中.
</code></span></code></pre>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int setcontext(const ucontext_t *ucp);  // 设置当前的上下文为 ucp
</code></span></code></pre>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>setcontext()</code></span> 的上下文 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>ucp</code></span> 应该通过 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>getcontext()</code></span> 或者 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>makecontext()</code></span> 取得，如果调用成功则不返回。</p>
<ul>
<li>如果上下文是通过调用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>getcontext()</code></span> 取得，程序会继续执行这个调用。</li>
<li>如果上下文是通过调用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>makecontext()</code></span> 取得，程序会调用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>makecontext()</code></span> 函数的第二个参数指向的函数。</li>
</ul>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
</code></span></code></pre>
<p>（需要先调用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>getcontext()</code></span> 取得 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>ucp</code></span>），然后调用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>makecontext()</code></span> 给该上下文指定一个栈空间 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>ucp-&gt;stack</code></span>，设置后继的上下文 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>ucp-&gt;uc_link</code></span>。当上下文通过 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>setcontext()</code></span> 或者 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>swapcontext()</code></span> 激活后，执行 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>func()</code></span> 函数，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>argc</code></span> 为 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>func()</code></span> 的参数个数，后面是<span style="overflow-x: auto; max-width:100%; display:inline;"><code>func()</code></span> 的参数序列。当 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>func()</code></span> 执行返回后，继承的上下文被激活，如果继承上下文为 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>NULL</code></span> 时，线程退出。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int swapcontext(ucontext_t *oucp, ucontext_t *ucp); // 保存当前上下文到 oucp 结构体中，然后激活 upc 上下文。 
</code></span></code></pre>
<p>如果执行成功，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>getcontext()</code></span> 返回 0，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>setcontext()</code></span> 和 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>swapcontext()</code></span> 不返回；</p>
<p>如果执行失败，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>getcontext()</code></span>，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>setcontext()</code></span>，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>swapcontext()</code></span> 返回-1，并设置对于的 errno.</p>
<h2 id="_2">简单说来</h2>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>getcontext</code></span>获取当前上下文。</p>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>setcontext</code></span>设置当前上下文。</p>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>swapcontext</code></span>切换上下文。</p>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>makecontext</code></span>创建一个新的上下文。</p>
<blockquote class="content-quote">
<p>待续，转载自：https://blog.csdn.net/qq910894904/article/details/41911175</p>
</blockquote>
<h1 id="c-protothreads">一个“蝇量级” C 语言协程库：<span style="overflow-x: auto; max-width:100%; display:inline;"><code>Protothreads</code></span></h1>
<p>Python<span style="overflow-x: auto; max-width:100%; display:inline;"><code>对协程的支持还非常有限，用在</code></span>generator<span style="overflow-x: auto; max-width:100%; display:inline;"><code>（生成器） 中的</code></span>yield` 可以一定程度上实现协程。虽然支持不完全，但已经可以发挥相当大的威力了。</p>
<p>来看例子：</p>
<p>传统的 <strong>生产者-消费者</strong> 模型是一个线程写消息，一个线程取消息，通过锁机制控制队列和等待，但一不小心就可能死锁。如果改用协程，生产者生产消息后，直接通过 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>yield</code></span> 跳转到消费者开始执行，待消费者执行完毕后，切换回生产者继续生产，效率极高：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-python"># python3
import time

def consumer():
    &quot;&quot;&quot;
    因为 consumer 函数中有 yield 关键字，所以foo函数并不会真的执行，
    而是先得到一个生成器 generator (相当于一个对象)
    &quot;&quot;&quot;
    while True:
        get_food = yield 'OK'
        &quot;&quot;&quot;
        yield 可以类似于一个 return，程序会返回 yield 右边的值，并在 yield 左边停下
        当下一次调用生成器 send 的方法时，send(arg) 中的参数 arg 会传递给 yield 左边的变量
        并继续执行，直到函数结束 return 或者 yield
        &quot;&quot;&quot;
        print('[consumer] Consuming %s...' % get_food)
        time.sleep(1)

def producer(c):
    c.__next__() # 启动生成器
    send_food = '(food)'
    print('[producer] Producing %s...' % send_food)
    r = c.send(send_food) # 通过 c.send(send_food) 切换到 consumer 执行
    print('[producer] Consumer return: %s' % r)
    c.close()

if __name__=='__main__':
    c = consumer()
    producer(c) 
</code></span></code></pre>
<p>consumer 函数是一个 generator（生成器），把一个 consumer 传入 produce 后：</p>
<ol>
<li>首先调用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>c.next()</code></span> 启动生成器；</li>
<li>然后，一旦生产了东西，通过 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>c.send(n)</code></span> 切换到 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>consumer</code></span> 执行；</li>
<li><span style="overflow-x: auto; max-width:100%; display:inline;"><code>consumer</code></span> 通过 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>yield</code></span> 拿到消息，处理，又通过 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>yield</code></span> 把结果传回；</li>
<li><span style="overflow-x: auto; max-width:100%; display:inline;"><code>produce</code></span> 拿到 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>consumer</code></span> 处理的结果，继续生产下一条消息；</li>
<li><span style="overflow-x: auto; max-width:100%; display:inline;"><code>produce</code></span> 决定不生产了，通过 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>c.close()</code></span> 关闭 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>consumer</code></span>，整个过程结束。</li>
</ol>
<h2 id="c-yield">C 语言的 &ldquo;<span style="overflow-x: auto; max-width:100%; display:inline;"><code>yield</code></span> 语义&rdquo;</h2>
<p>我们知道 python 的 yield 语义功能类似于一种迭代生成器，函数会保留上次的调用状态，并在下次调用时会从上个返回点继续执行。用 C 语言来写就像这样：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int function(void) {
    for (int i = 0; i &lt; 10; i++)
        return i;   /* won't work, but wouldn't it be nice */
}
</code></span></code></pre>
<p>连续对它调用 10 次，它能分别返回 0 到 9。</p>
<p>该怎样实现呢？可以利用 goto 语句，如果我们在函数中加入一个状态变量，就可以这样实现：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int function(void) {
    static int i;
    static int state = 0;
    switch (state) {
        case 0: goto LABEL0;
        case 1: goto LABEL1;
    }
    LABEL0: /* start of function */
    for (i = 0; i &lt; 10; i++) {
        state = 1; /* so we will come back to LABEL1 */
        return i;
        LABEL1:; /* resume control straight after the return */
    }
    return 0;
}
</code></span></code></pre>
<p>其实我们可以不用 switch 语句来决定要跳转到哪里去执行，而是<strong>直接利用 switch 语句本身来实现跳转</strong>：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int function(void) {
    static int i
    static int state = 0;
    switch (state) {
        case 0: /* start of function */
            for (i = 0; i &lt; 10; i++) {
                state = 1; /* so we will come back to &quot;case 1&quot; */
                return i;
                case 1:; /* resume control straight after the return */
            }
    }
    return 0;
}
</code></span></code></pre>
<p>使用宏 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>__LINE__</code></span> 一般化：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">int function(void) {
    static int i;
    static int state = 0;
    switch (state) {
        case 0: /* start of function */
            for (i = 0; i &lt; 10; i++) {
                state = __LINE__ + 2; /* so we will come back to &quot;case __LINE__&quot; */
                return i;
                case __LINE__:; /* resume control straight after the return */
            }
    }
    return 0;
}
</code></span></code></pre>
<p>使用宏定义来进行抽象：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#define Begin()     static int state = 0;   \
                    switch(state) {         \
                        case 0:

/* 注意：__LINE__ 不需要 +2 因为 Yield() 中的两个 __LINE__ 在同一行 */
#define Yield(x)    do { \
                        state = __LINE__;   \
                        return x;           \
                        case __LINE__:;     \
                    } while (0)

#define End() }

int function(void)
{
    static int i;
    Begin();
    for (i = 0; i &lt; 10; i++) {
        Yield(i);
    }
    End();
    return 0;
}
</code></span></code></pre>
<h2 id="protothreads">Protothreads的原语和组件</h2>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#define LC_INIT(s)      s = 0;
#define LC_RESUME(s)    switch (s) { case 0:
#define LC_SET(s)       s = __LINE__; case __LINE__:
#define LC_END(s)       }
</code></span></code></pre>
<p>但这种“原语”有个难以察觉的缺陷：<strong>就是你无法在 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>LC_RESUME</code></span> 和 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>LC_END</code></span> （或者包含它们的组件）之间的代码中使用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>switch-case</code></span> 语句，因为这会引起外围的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>switch</code></span> 跳转错误！</strong>为此，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>protothreads</code></span> 又实现了基于 GNU C 的调度“原语”。在 GNU C 下还有一种语法糖叫做标签指针，就是在一个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>label</code></span> 前面加 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>&amp;&amp;</code></span>（不是地址的地址，是 GNU 自定义的符号），可以用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>void</code></span> 指针类型保存，然后 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>goto</code></span> 跳转：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">typedef void * lc_t；
#define LC_INIT(s)          s = NULL

#define LC_RESUME(s)        do {                    \
                                if (s != NULL) {    \
                                    goto *s;        \
                                }                   \
                            } while (0)

#define LC_CONCAT2(s1, s2)  s1##s2

#define LC_CONCAT(s1, s2)   LC_CONCAT2(s1, s2)

#define LC_SET(s)           do {                                        \
                                LC_CONCAT(LC_LABEL, __LINE__):          \
                                (s) = &amp;&amp;LC_CONCAT(LC_LABEL, __LINE__);  \
                            } while (0)
</code></span></code></pre>
<p>好了，有了前面的基础知识，理解这些“原语”就是小菜一叠，下面看看如何建立“组件”，同时也是 protothreads API，我们先定义四个退出码作为协程的<strong>调度状态机</strong>：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#define PT_WAITING 0
#define PT_YIELDED 1
#define PT_EXITED  2
#define PT_ENDED   3
</code></span></code></pre>
<p>下面这些 API 可直接在应用程序中调用：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">/* 初始化一个协程，也即初始化状态变量 */
#define PT_INIT(pt)                     LC_INIT((pt)-&gt;lc)

/* 声明一个函数，返回值为 char 即退出码，表示函数体内使用了 proto thread，（个人觉得有些多此一举） */
#define PT_THREAD(name_args)            char name_args

/* 协程入口点， PT_YIELD_FLAG=0表示出让，=1表示不出让，放在 switch 语句前面，下次调用的时候可以跳转到上次出让点继续执行 */
#define PT_BEGIN(pt)                    {                           \
                                            char PT_YIELD_FLAG = 1; \
                                            LC_RESUME((pt)-&gt;lc)

/* 协程退出点，至此一个协程算是终止了，清空所有上下文和标志 */
#define PT_END(pt)                          LC_END((pt)-&gt;lc);       \
                                            PT_YIELD_FLAG = 0;      \
                                            PT_INIT(pt);            \
                                            return PT_ENDED;        \
                                        }

/* 协程出让点，如果此时协程状态变量 lc 已经变为 __LINE__ 跳转过来的，那么 PT_YIELD_FLAG = 1，表示从出让点继续执行。 */
#define PT_YIELD(pt)                    do {                            \
                                            PT_YIELD_FLAG = 0;          \
                                            LC_SET((pt)-&gt;lc);           \
                                            if(PT_YIELD_FLAG == 0) {    \
                                                return PT_YIELDED;      \
                                            }                           \
                                        } while(0)

/* 附加出让条件 */
#define PT_YIELD_UNTIL(pt, cond)        do {                                        \
                                            PT_YIELD_FLAG = 0;                      \
                                            LC_SET((pt)-&gt;lc);                       \
                                            if((PT_YIELD_FLAG == 0) || !(cond)) {   \
                                                return PT_YIELDED;                  \
                                            }                                       \
                                        } while(0)

/* 协程阻塞点(blocking),本质上等同于 PT_YIELD_UNTIL，只不过退出码是 PT_WAITING，用来模拟信号量同步 */
#define PT_WAIT_UNTIL(pt, condition)    do {                            \
                                            LC_SET((pt)-&gt;lc);           \
                                            if (!(condition)) {         \
                                                return PT_WAITING;      \
                                            }                           \
                                        } while(0)

/* 同 PT_WAIT_UNTIL 条件反转 */
#define PT_WAIT_WHILE(pt, cond)         PT_WAIT_UNTIL((pt), !(cond))

/* 协程调度，调用协程 f 并检查它的退出码，直到协程终止返回 0，否则返回 1。 */
#define PT_SCHEDULE(f)                  ((f) &lt; PT_EXITED)

/* 这用于非对称协程，调用者是主协程，pt 是和子协程 thread （可以是多个）关联的上下文句柄，主协程阻塞自己调度子协程，直到所有子协程终止 */
#define PT_WAIT_THREAD(pt, thread)      PT_WAIT_WHILE((pt), PT_SCHEDULE(thread))

/* 用于协程嵌套调度，child 是子协程的上下文句柄 */
#define PT_SPAWN(pt, child, thread)     do {                                    \
                                            PT_INIT((child));                   \
                                            PT_WAIT_THREAD((pt), (thread));     \
                                        } while(0)
</code></span></code></pre>
<p>暂时介绍这么多，用户还可以根据自己的需求随意扩展组件，比如实现信号量，你会发现脱离了操作系统环境下的信号量竟是如此简单：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">struct pt_sem {
    unsigned int count;
};

#define PT_SEM_INIT(s, c)       (s)-&gt;count = c

#define PT_SEM_WAIT(pt, s)      do {                                    \
                                    PT_WAIT_UNTIL(pt, (s)-&gt;count &gt; 0);  \
                                    --(s)-&gt;count;                       \
                                } while(0)

#define PT_SEM_SIGNAL(pt, s)    ++(s)-&gt;count
</code></span></code></pre>
<blockquote class="content-quote">
<p>待续，转载自：https://coolshell.cn/articles/10975.html</p>
</blockquote>
  </div>
</div>
 
    </div>
  </div>
  <div id="footer-background">
    <div id="footer">
      <div class="legal pure-g">
  <div class="pure-u-1 u-sm-1-2">
    <p class="legal-license"><a href="https://beian.miit.gov.cn/#/Integrated/index">浙ICP备2020038748号</a></p>
  </div>
  <div class="pure-u-1 u-sm-1-2">
    <p class="legal-links"><a href="https://github.com/zromyk">GitHub</a></p>
    <p class="legal-copyright">Copyright © 2021 Wei Zhou. 保留所有权利。</p>
  </div>
</div>
    </div>
  </div>
  <!-- <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> -->
  <script src="/style/html/jquery.min.js"></script>
  <script src='/style/article/latex/latest.js?config=TeX-MML-AM_CHTML'></script>
<!-- <script src="https://cdn.geogebra.org/apps/deployggb.js"></script> -->
<script src="/style/article/deployggb.js"></script>
<!-- <script src="https://apps.bdimg.com/libs/highlight.js/9.1.0/highlight.min.js"></script> -->
<script type="text/javascript">
  // 脚本：navigation 随鼠标移动自动变换宽度
  var element = document.getElementById("navigation"); // 获取要操作的元素
  var elementWidth = parseInt(getComputedStyle(element).width);
  var elementLeft = 0;
  var elementRight = 0;
  element.addEventListener('mouseenter', function (event) { // 添加鼠标按下事件的监听器
    elementLeft = element.getBoundingClientRect().left - 10;
    elementRight = element.getBoundingClientRect().left + elementWidth * 3;
    window.addEventListener('mousemove', resize); // 添加全局的鼠标移动事件的监听器
  });

  function resize(event) {
    var minWidth = elementWidth;
    var maxWidth = elementWidth * 2.5;
    // console.log(elementLeft, event.clientX, elementRight, event.clientX - elementLeft + elementWidth / 2);
    if (elementLeft <= event.clientX && event.clientX <= elementRight) {
      var width = event.clientX - elementLeft + elementWidth / 2;
      width = Math.min(width, maxWidth);
      width = Math.max(width, minWidth);
      element.style.width = width + 'px'; // 设置新的宽度样式属性
    }
    else {
      element.style.width = elementWidth + 'px'; // 设置新的宽度样式属性
      stopResize();
    }
  }

  function stopResize() {
    element.style.width = elementWidth + 'px'; // 设置新的宽度样式属性
    // console.log("stopResize", elementLeft, event.clientX, elementRight, event.clientX - elementLeft + elementWidth / 2);
    window.removeEventListener('mousemove', resize); // 移除鼠标移动事件的监听器
  }
</script>
<script src="/style/article/highlight/highlight.min.js"></script>
<script type="text/javascript">
  // 脚本：code语法高亮
  hljs.initHighlightingOnLoad();
</script>
<script>
  function animateByNav() {
    $("html").animate({
        scrollTop: ($(event.target.hash).offset().top - 52)
    }, 300);
  };
</script>
<script src="/style/article/pell-1.0.6/dist/pell.js"></script>
<script>
  // 脚本：自由编辑页面
  var editor = window.pell.init({
    element: document.getElementById('editor'),
    defaultParagraphSeparator: 'p',
    onChange: function(html) {
        document.getElementById('text-output').innerHTML = html
        document.getElementById('html-output').textContent = html
    }
  });

  function markdownEditor() {
    var articles = document.getElementById('content-articles-markdown');
    if (articles.getAttribute("contenteditable") == "true") {
        articles.setAttribute("contenteditable", "false");
        document.getElementById("content-articles-markdownEditor").style.display = "none"; //隐藏
        document.getElementById("button-markdownEditor").innerHTML = "启用编辑";
    } else {
        articles.setAttribute("contenteditable", "true");
        document.getElementById("content-articles-markdownEditor").style.display = ""; //显示
        document.getElementById("button-markdownEditor").innerHTML = "关闭编辑";
    }
  };

  function save() {
      window.alert("保存成功");
  };
</script>

</body>
</html>
